#include "Core.h"

stEvent *gEventChain[PRI_LEVELS];

#define RET_RESCHEDULE 1

stCoreFlag gCoreFlag;
stCoreData gCoreData;


const UInt8 null[] = {0,0,0,0};

static void Core_EventInitBuf(void)
{
    UInt8 i;
    for (i = 0; i < PRI_LEVELS; i ++)
    {
    	gEventChain[i] = NULL;
    }

    gCoreData.EventPriovity   = PRI_REALTIME;
    gCoreData.EventReschedule = FALSE;
}

static stEvent * Core_FindPreEvent(UInt8 Priority)
{
    stEvent *p = gEventChain[Priority];
    while (p->Next != gEventChain[Priority]) {
        p = p->Next;
    }
    return p;
}

static void Core_FreeEvent(UInt8 Priority,stEvent *pEvent)
{
    LOCK_INTERRUPT;
    if (pEvent->Next == pEvent) {
        gEventChain[Priority] = NULL;
    } else {
        stEvent *p = Core_FindPreEvent(Priority);
        p->Next = pEvent->Next;
        gEventChain[Priority] = pEvent->Next;
    }
    Heap_Release(pEvent);
    UNLOCK_INTERRUPT;
}

static void Core_EventSchedule(UInt8 Priority)
{
    while (gEventChain[Priority] != NULL) {
        stEvent *pEvent;
        stModuleInterface const * pInterface;

        pEvent = gEventChain[Priority];
        pInterface = gModuleInterface[pEvent->AppIndex];

        if ((pInterface!= NULL) && (pInterface->ModuleEventHandler != NULL))
        {
            RESET_WATCHDOG;
            if (RET_EVENT_STOP == pInterface->ModuleEventHandler(pEvent->Code,&pEvent->Param))
            {
                if (pEvent->fpOnEventDone != NULL)
                {
                    pEvent->fpOnEventDone(&pEvent->Param);
                }
                Core_FreeEvent(Priority,pEvent);
            }
            else
            {
                pEvent->AppIndex ++;
                if (gModuleInterface[pEvent->AppIndex] == NULL)
                {
                    if (pEvent->fpOnEventDone != NULL)
                    {
                        pEvent->fpOnEventDone(&pEvent->Param);
                    }
                    Core_FreeEvent(Priority,pEvent);
                }
            }
        }

        if (gEventChain[Priority] != NULL) {
            gEventChain[Priority] = gEventChain[Priority]->Next;
        }
        if (Priority == PRI_REALTIME)
        {
            if (gCoreData.RTEventReschedule == TRUE) {
                gCoreData.RTEventReschedule = FALSE;
                break;
            }
        }
        else
        {
            if (gCoreData.EventReschedule == TRUE) {
                gCoreData.EventReschedule = FALSE;
                break;
            }
        }

    }
}


static void Core_RunRTEvent(void)
{
    if (gCoreData.RTEventRunning == YES)
    {
        return;
    }

    gCoreData.RTEventRunning = YES;
    Core_EventSchedule(PRI_REALTIME);
    gCoreData.RTEventRunning = NO;
}

void Core_EventRun(void)
{
    if (gCoreData.EventReschedule == TRUE)
    {
        gCoreData.EventPriovity = PRI_HIGHEST;
        gCoreData.EventReschedule = FALSE;
    }

    Core_EventSchedule(gCoreData.EventPriovity);

    gCoreData.EventPriovity ++;
    if (gCoreData.EventPriovity == PRI_REALTIME)
    {
        gCoreData.EventPriovity ++;
    }
}

UInt8 Core_EventRaise(UInt8 Priority,UInt16 EventCode,stParam *pEventParam,TfpCoreEvent fpOnEventDone)
{
	stEvent * pEvent;
	void * p;

	if (Priority > PRI_LEVELS){
		return RET_INVALID_PRI;
	}

	p = Heap_Request(sizeof(stEvent));

	if (p != NULL) {
		pEvent = (stEvent *)p;

		LOCK_INTERRUPT;
		pEvent->AppIndex = 0;
		pEvent->Code = EventCode;
		pEvent->fpOnEventDone = fpOnEventDone;

        if (pEventParam == NULL)
        {
            pEvent->Param.Long.Value = 0;
        }
        else
        {
            pEvent->Param = *pEventParam;
        }

		if (gEventChain[Priority] == NULL) {
			gEventChain[Priority] = pEvent;
			pEvent->Next = pEvent;
		} else {
			pEvent->Next = gEventChain[Priority]->Next;
			gEventChain[Priority]->Next = pEvent;
		}

		if (Priority == PRI_REALTIME)
        {
            gCoreData.RTEventReschedule = TRUE;
        }
        else
        {
            gCoreData.EventReschedule = TRUE;
        }
        gCoreData.RandomSeed ++;
		UNLOCK_INTERRUPT;
		return RET_OK;
	} else {
		return RET_NOMEMARY;
	}
}

static void Core_RunTickInterrupt(void)
{
    UInt8 i;
    for (i = 0; i < 255; i++)
    {
        if (gModuleInterface[i] == NULL)
        {
            break;
        }
        else
        {
            if (gModuleInterface[i]->OnTickEvent != NULL)
            {
                gModuleInterface[i]->OnTickEvent();
            }
        }
    }

    ENABLE_INTERRUPT;
    Core_RunRTEvent();
}

void Core_TickService(void *pTimer)
{
    gCoreData.Timer.Count = CORE_TIMER_10MS;
    gCoreData.Timer.OnTimeOut = Core_TickService;
    Core_SetTimer(&gCoreData.Timer);

    if (gCoreFlag.RunState == RUN_NORMAL) {
        gCoreData.CounterMs += TIME_TICK;
        Core_RunTickInterrupt();
    }
}

static void Core_IdelEvent(stParam *pEventParam)
{
    Core_EventRaise(PRI_LOWEST,EVENT_CORE_IDLE,pEventParam,Core_IdelEvent);
}


static void Core_OnInitDone(stParam *pEventParam)
{
    gCoreFlag.RunState = RUN_NORMAL;
    Core_EventRaise(PRI_LOWEST,EVENT_CORE_IDLE,NULL,Core_IdelEvent);
}

static void Core_Init(void)
{
    Heap_Init();
    Core_TimerInit();
    gCoreData.Timer.Count = CORE_TIMER_10MS;
    gCoreData.Timer.OnTimeOut = Core_TickService;
    Core_SetTimer(&gCoreData.Timer);

    gCoreFlag.RunState = RUN_INIT;
    Core_EventInitBuf();
    Core_EventRaise(PRI_HIGH,EVENT_CORE_INIT,NULL,Core_OnInitDone);
}

void Core_TimerEvent(stParam *pEventParam)
{
    if (gCoreData.CounterMs >= 10)
    {
        gCoreData.CounterFor100Ms ++;
        gCoreData.CounterFor500Ms ++;
        gCoreData.CounterForSec   ++;
        Core_EventRaise(PRI_HIGH,EVENT_CORE_10MS,NULL,Core_TimerEvent);
        DISABLE_INTERRUPT;
        gCoreData.CounterMs -= 10;
        ENABLE_INTERRUPT;
    }

    if (gCoreData.CounterFor100Ms >= 10)
    {
        Core_EventRaise(PRI_HIGH,EVENT_CORE_100MS,NULL,NULL);
        gCoreData.CounterFor100Ms -= 10;
    }

    if (gCoreData.CounterFor500Ms >= 50)
    {
        Core_EventRaise(PRI_NORMAL,EVENT_CORE_500MS,NULL,NULL);
        gCoreData.CounterFor500Ms -= 50;
    }

    if (gCoreData.CounterForSec >= 100)
    {
        Core_EventRaise(PRI_NORMAL,EVENT_CORE_SEC,NULL,NULL);
        gCoreData.CounterForSec -= 100;
    }
}

void Core_Run(void)
{
    Core_Init();

    for (;;)
    {
        RESET_WATCHDOG;
        Core_TimerEvent(NULL);
        Core_EventRun();
    }
}

UInt16 Core_GetRandom(UInt16 n)
{
    UInt32 a;
    UInt16 ret;
    gCoreData.RandomSeed += CORE_TIMER_VAL;
    a += gCoreData.RandomSeed;
    a = a * a * 31489;
    a >>= 8;
    ret = a;
    return (ret % n);
}

